home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection 1998 Fall: Game Toolkit / Disc.iso / Samples / Moofwars 1.02 / MoofWars Sprocket / •Source / MoofWars.cp next >
Encoding:
Text File  |  1998-08-13  |  22.9 KB  |  808 lines  |  [TEXT/CWIE]

  1. /*************************************************************************************
  2. MoofWars.cp
  3.  
  4. All of the main game functions are defined in here, including initialization, tear-down
  5. and the actual game loop.
  6.  
  7. Author: Timothy Carroll
  8. Apple Developer Technical Support
  9. timc@apple.com
  10.  
  11. Modification History: 
  12.  
  13. 4/2/97        TMC        Now dispose of the color table when we exit the game.  Technically,
  14. we still end up leaking the appPalette, so this should be corrected if InitGame is called
  15. more than once.
  16.  
  17. 4/2/97        TMC        Now check DSpStartup error code.
  18. 2/24/97        TMC        Now conditionally allocate QD globals if we aren't using Metrowerks
  19. 1/23/97        TMC        allocate QuickDraw globals so that MrC will compile.
  20. 1/23/97     TMC        Added include for Moofwars.h so that MrC will compile
  21. 1/23/97        TMC        Now include other system headers so that MrC will compile.
  22. 8/15/96        TMC     Initial Release
  23.  
  24. Copyright © 1996, 1997 Apple Computer, Inc., All Rights Reserved
  25.  
  26.  
  27. You may incorporate this sample code into your applications without
  28. restriction, though the sample code has been provided "AS IS" and the
  29. responsibility for its operation is 100% yours.  However, what you are
  30. not permitted to do is to redistribute the source as "DSC Sample Code"
  31. after having made changes. If you're going to re-distribute the source,
  32. we require that you make it clear in the source that the code was
  33. descended from Apple Sample Code, but that you've made changes.
  34.  
  35. *************************************************************************************/
  36.  
  37.  
  38.  
  39. #if __profile__
  40. #include <profiler.h>
  41. #endif
  42.  
  43. #include <fp.h>
  44. #include <QuickDraw.h>
  45. #include <Fonts.h>
  46. #include <Sound.h>
  47.  
  48.  
  49. #include "Moofwars.h"
  50. #include "TTileGrid.h"
  51. #include "TSpriteCollection.h"
  52. #include "TShipSprite.h"
  53. #include "TEnemySprite.h"
  54. #include "TShotSprite.h"
  55.  
  56. #if qShowTiming
  57. #include <Timer.h>
  58. #include <FixMath.h>
  59. #include <TextUtils.h>
  60. #endif
  61.  
  62. /********************************************************************************
  63.     GLOBALS
  64.  ********************************************************************************/
  65.  
  66. // Quickdraw Globals
  67. #if TARGET_RT_MAC_CFM
  68. #ifndef  __MWERKS__
  69. QDGlobals    qd;
  70. #endif
  71. #endif
  72.  
  73. CTabHandle                gAppColorTable = NULL;
  74. DSpContextReference        gDrawContext = NULL;
  75.  
  76. // We define a few static variables to hold all the game information.  Only in a few cases
  77. // do we actually need to export this information to other functions.  In general, if a
  78. // particular object needs a piece of information, it should be sent directly to that object,
  79. // rather than be imported as a global.
  80.  
  81. // The playing field
  82. static TTileGrid        *theGrid = NULL;
  83. WorldRect32             gWorldBounds;
  84.  
  85. // All of the sprite groups
  86. static TSpriteCollection        *gFriendlyShipGroup = NULL;
  87. static TSpriteCollection        *gEnemyShipGroup = NULL;
  88. static TSpriteCollection        *gFriendlyShotGroup = NULL;
  89. static TSpriteCollection        *gEnemyShotGroup = NULL;
  90.  
  91. // We keep track of the two ships separately as well,
  92. // as we can use them as the camera location
  93. static TShipSprite        *gFriendlyShip = NULL;
  94. static TEnemySprite        *gEnemyShip = NULL;
  95.  
  96. // Although the sprites would load their own graphics (because of the magic of our TGraphic
  97. // collection, we can preload the graphics and pass them to the TSprite constructors for
  98. // speed.  These variables will manage the loading and unloading of the sprites.
  99.  
  100. TGraphicCollection *gShipGraphics = NULL;
  101. TGraphicCollection *gEnemyGraphics = NULL;
  102. TGraphicCollection *gShotGraphics = NULL;
  103.  
  104. #if qUsingSound
  105. // Sounds
  106. SndReference     gSounds[10];
  107. Boolean         gPlayingSound = true; // If false, no sounds will play
  108. #endif
  109.  
  110. #if qShowTiming
  111. // Miscellaneous
  112. long             gShotsOnBoard = 0;  // Holds the total number of shots in game
  113. #endif
  114.  
  115. // Holds the current state of the keyboard and other input devices when processing
  116. // the list of sprites.
  117. TInputState         gCommandKeys;
  118. TCommandTimer        theTimer;
  119.  
  120. // A standard set of lookup tables in 16.16 fixed notation, so that we can quickly
  121. // do sin and cos.  We currently use 48 frames because thats easy to do from
  122. // Specular Infini-D.  360/48 = 7.5° per angle.
  123. SInt32             gSinLookup[48];
  124. SInt32            gCosLookup[48];
  125.  
  126.  
  127. /********************************************************************************
  128.     GLOBALS
  129.  ********************************************************************************/
  130.  
  131. void main (void);
  132.  
  133. // Initialize everything for the game
  134. static OSStatus InitGame (void);
  135.  
  136. // Fills in the initial values in the lookup tables.
  137. static OSStatus InitAngleLookups (void);
  138.  
  139. #if qUsingSound
  140. // Initializes Sound API and loads all the sound resources
  141. static OSStatus LoadSounds (void);
  142. #endif
  143.  
  144. // This creates a draw sprocket context
  145. static OSStatus InitDrawSprocket (void);
  146.  
  147. // This loads all of our graphics and tile resources
  148. static OSStatus LoadGameGraphics (void);
  149.  
  150. // This creates the grid, the initial sprites and sprite groups.
  151. static OSStatus CreateGame (void);
  152.  
  153. // The actual event loop
  154. static OSStatus RunGame (void);
  155.  
  156. // The cleanup code for InitGame
  157. static OSStatus CloseGame (void);
  158.  
  159.  
  160. /********************************************************************************
  161.     main
  162.     
  163.     All main does is the standard mac init, followed by initializing the game
  164.     and running it once.  In a real game it would probably put up a splash screen
  165.     with some widgets on it.
  166.  ********************************************************************************/
  167. void main (void)
  168. {
  169.  
  170.     int            loop;
  171.     OSStatus    theErr = noErr;
  172.  
  173.     InitGraf(&qd.thePort);
  174.     InitFonts();
  175.     InitWindows();
  176.     InitMenus();
  177.     TEInit();
  178.     InitDialogs(nil);
  179.     InitCursor();
  180.  
  181.     MaxApplZone();
  182.     
  183.     for (loop = 0; loop < 30; loop++)
  184.         MoreMasters();
  185.         
  186.     theErr = InitGame();
  187.     FAIL_OSERR (theErr, "\pCouldn't initialize new game")
  188.     theErr = RunGame();
  189.     FAIL_OSERR (theErr, "\pFailure in game running code")
  190.     
  191.     error:
  192.     cleanup:
  193.         theErr= CloseGame();
  194. }
  195.  
  196.  
  197. /************************************************************************************
  198.  InitGame
  199.  
  200.  Init Game loads all the sprites and sets up all the variables for running a game.
  201.  Eventually, we can split out a large number of these routines so that they only
  202.  need to be called when the application actually initializes, rather than once per game.
  203.  
  204.  Of course, our loading routines are way cool, so we may not care. :)
  205.  
  206.  
  207.  ************************************************************************************/
  208.  
  209. OSStatus InitGame (void)
  210. {
  211.     OSStatus                 theErr = noErr;
  212.     PaletteHandle             appPalette;
  213.  
  214.     // Load our initial color table and make it the default palette for the entire application.
  215.     // (We'll also send this color table to DrawSprocket)
  216.     
  217.     gAppColorTable = GetCTable (kAppColorTableResID);
  218.     FAIL_NIL (gAppColorTable, "\pFailed to load the color table")
  219.     appPalette = NewPalette (256, gAppColorTable, pmExplicit + pmTolerant, 0);
  220.     FAIL_NIL (appPalette, "\pFailed to convert the color table into a palette")
  221.     SetPalette ( (WindowRef) -1L, appPalette, false);    
  222.  
  223.     // Initialize the sound effects
  224. #if qUsingSound
  225.     theErr = LoadSounds();
  226.     FAIL_OSERR (theErr, "\pFailed to load the sounds")
  227. #endif
  228.     
  229. // Initialize our predefined arrays of the move angles.
  230.     theErr = InitAngleLookups();
  231.     FAIL_OSERR (theErr, "\pFailed to initialize the lookup tables")
  232.  
  233. // Initialize the keyboard command routines.
  234.  
  235. #if qSyncTiming
  236.     theTimer.Initialize(30); // 30 frames per second
  237. #else
  238.     theTimer.Initialize (0); // one command per frame, fast as possible.
  239. #endif
  240.  
  241. // Create a draw sprocket context
  242.     theErr = InitDrawSprocket ();
  243.     FAIL_OSERR (theErr, "\pFailed to initialize draw sprocket")
  244.  
  245. // Load the game graphics
  246.     theErr = LoadGameGraphics();
  247.     FAIL_OSERR (theErr, "\pFailed to preload the game graphics")
  248.  
  249. // Create all of the game objects -- note that the stuff in here is probably the only thing that
  250. // would be run more than once every time the application runs -- the rest of the initialization code
  251. // will all run once.
  252.     theErr = CreateGame();
  253.     FAIL_OSERR (theErr, "\pFailed to create the game objects")
  254.     
  255.     return noErr;
  256.     
  257.     error:
  258.     
  259.     if (theErr == noErr)
  260.         theErr = paramErr;
  261.     return theErr;    
  262. }
  263.  
  264. /************************************************************************************
  265. InitAngleLookups 
  266.  
  267.  ************************************************************************************/
  268.  
  269. OSStatus InitAngleLookups(void)
  270. {
  271.     int loop;
  272.     
  273.     for (loop = 0; loop < 48; loop++)
  274.     {
  275.         double angle = (90 - loop * 7.5)/57.296;
  276.         
  277.         gSinLookup[loop] = (1<<14) * sin (angle);
  278.         gCosLookup[loop] = (1<<14) * -cos (angle);
  279.     }
  280.  
  281.     return noErr;
  282. }
  283.  
  284.  
  285. /************************************************************************************
  286.  LoadSounds
  287.  
  288. This initializes whatever sound API we're using and loads all of the sound resources.
  289.  
  290.  ************************************************************************************/
  291.  
  292. #if qUsingSound
  293. OSStatus LoadSounds (void)
  294. {
  295.     OSStatus theErr;
  296.     // Initialize and load all of the game sounds.
  297.  
  298.     theErr = HY_Setup (kNormal, 5);
  299.     FAIL_OSERR (theErr, "\p Failed to set up hollywood API")
  300.     gSounds[0] = HY_GetSoundResource(6000);
  301.     FAIL_NIL(gSounds[0], "\p Failed to allocate the sound resource")
  302.     gSounds[1] = HY_GetSoundResource(6001);
  303.     FAIL_NIL(gSounds[1], "\p Failed to allocate the sound resource")
  304.     gSounds[2] = HY_GetSoundResource(6002);
  305.     FAIL_NIL(gSounds[2], "\p Failed to allocate the sound resource")
  306.     gSounds[3] = HY_GetSoundResource(6003);
  307.     FAIL_NIL(gSounds[3], "\p Failed to allocate the sound resource")
  308.     gSounds[4] = HY_GetSoundResource(6004);
  309.     FAIL_NIL(gSounds[4], "\p Failed to allocate the sound resource")
  310.     gSounds[5] = HY_GetSoundResource(6005);
  311.     FAIL_NIL(gSounds[5], "\p Failed to allocate the sound resource")
  312.     
  313.     return noErr;
  314.     
  315.     error:
  316.     if (theErr == noErr)
  317.         theErr = paramErr;
  318.     return theErr;    
  319. }
  320. #endif
  321.  
  322.  
  323. /************************************************************************************
  324. InitDrawSprocket 
  325.  
  326.  ************************************************************************************/
  327. OSStatus InitDrawSprocket (void)
  328. {
  329.     OSStatus                 theErr = noErr;
  330.     DSpContextAttributes    screenData;
  331.     RGBColor                fade;
  332.     
  333. // Create a Draw Sprocket context for the screen.
  334.     theErr = DSpStartup();
  335.     FAIL_OSERR (theErr, "\pError: Failed to start up DrawSprocket")
  336.     
  337.     // fill in our requirements.  Anything we place here we need to have for our game to work properly.
  338.     screenData.frequency                = 0;
  339.     screenData.displayWidth                = 640;
  340.     screenData.displayHeight            = 480;
  341.     screenData.reserved1                = 0;
  342.     screenData.reserved2                = 0;
  343.     screenData.colorNeeds                = kDSpColorNeeds_Require;
  344.     screenData.colorTable                = gAppColorTable;
  345.     screenData.contextOptions            = 0;
  346.     screenData.backBufferDepthMask        = kDSpDepthMask_8;
  347.     screenData.displayDepthMask            = kDSpDepthMask_8;
  348.     screenData.backBufferBestDepth        = 8;
  349.     screenData.displayBestDepth            = 8;
  350.     screenData.pageCount                = 2;
  351.     screenData.gameMustConfirmSwitch    = false;
  352.     screenData.reserved3[0]                = 0;
  353.     screenData.reserved3[1]                = 0;
  354.     screenData.reserved3[2]                = 0;
  355.     screenData.reserved3[3]                = 0;
  356.     
  357.     theErr = DSpUserSelectContext(&screenData, 0,NULL, &gDrawContext);
  358.     FAIL_OSERR (theErr, "\p Failed to retrieve a drawing context")
  359.     
  360.     // We should now alter the screenData to reflect things that would be useful to have, but aren't
  361.     // requirements.
  362.     
  363. #if qUsePageFlip
  364.     screenData.contextOptions    |= kDSpContextOption_PageFlip;
  365.     screenData.pageCount        = 3;
  366. #endif
  367.  
  368. #if (!qSyncToDrawVBL)
  369.     screenData.contextOptions    |= kDSpContextOption_DontSyncVBL;
  370. #endif
  371.     
  372.     // Reserve the screen and set it to active.
  373.     theErr = DSpContext_Reserve (gDrawContext, &screenData);
  374.     FAIL_OSERR (theErr, "\pFailed to reserve the context")
  375. #if 0
  376.     theErr = DSpContext_SetMaxFrameRate (gDrawContext, 60);
  377.     FAIL_OSERR (theErr, "\p Failed to set the maximum frame rate")
  378. #endif    
  379.  
  380.     fade = kBlack; // DrawSprocket requires this to be a non-const param
  381.  
  382.     theErr = DSpContext_FadeGammaOut (gDrawContext, &fade);
  383.     FAIL_OSERR (theErr, "\p Failed to fade out")
  384.  
  385.     theErr = DSpContext_SetState (gDrawContext, kDSpContextState_Active);
  386.     FAIL_OSERR (theErr, "\pFailed to activate the draw context")
  387.  
  388.     theErr = DSpContext_FadeGammaIn (gDrawContext, &fade);
  389.     FAIL_OSERR (theErr, "\p Failed to fade in")
  390.  
  391.     return noErr;
  392.     
  393.     error:
  394.     if (theErr == noErr)
  395.         theErr = paramErr;
  396.     return theErr;
  397. }
  398.  
  399.  
  400. /************************************************************************************
  401. LoadGameGraphics 
  402.  ************************************************************************************/
  403.  
  404. OSStatus LoadGameGraphics (void)
  405. {
  406.     OSStatus theErr = noErr;
  407. // Preload and lock all of our game's graphics so that everything runs smoothly.
  408.     
  409.     gShipGraphics = TGraphicCollection::NewCollection (kShipResourceID);
  410.     FAIL_NIL (gShipGraphics, "\pcouldn't load friendly ship graphics")
  411.     gShipGraphics->LockCollection();
  412.     
  413.     gShotGraphics = TGraphicCollection::NewCollection (kShotResourceID);
  414.     FAIL_NIL (gShotGraphics, "\pcouldn't load friendly shot graphics")
  415.     gShotGraphics->LockCollection();
  416.  
  417. // This version uses the same graphics for the enemy and friendly units, but you can
  418. // insert your own favorite graphics instead.    
  419.     #if 0
  420.     gEnemyGraphics = TGraphicCollection::NewCollection (kEnemyResourceID);
  421.     FAIL_NIL (gEnemyGraphics, "\pcouldn't load enemy graphics")
  422.     gEnemyGraphics->LockCollection();
  423. #endif
  424.  
  425.     return noErr;
  426.     
  427.     error:
  428.     if (theErr == noErr)
  429.         theErr = paramErr;
  430.     return theErr;
  431. }
  432.  
  433.  
  434. /************************************************************************************
  435. CreateGame 
  436.  
  437.  ************************************************************************************/
  438. OSStatus CreateGame (void)
  439. {
  440.     OSStatus            theErr = noErr;
  441.     Rect                 boundsRect     = {0,0, 480, 640};
  442.     
  443.     TShipSpriteData        shipData;
  444.     TEnemySpriteData    enemyData;
  445.  
  446. // Load the Grid and Tiles.  Note that we do load the tile graphics here as well --
  447. // we could move that to LoadGameGraphics if we wanted.
  448. // Load the tile grid information
  449.     theGrid = new TTileGrid();
  450.     theErr = theGrid->CreateGridFromResource(kGridResourceID);
  451.     FAIL_OSERR (theErr, "\p Failed to load the grid resource")
  452.     
  453.     // Initialize the world boundaries in 16.16 fixed point coordinates.
  454.     
  455.     gWorldBounds.left = 0;
  456.     gWorldBounds.top = 0;
  457.     gWorldBounds.right = theGrid->GetGridWidth() * (32 << 16);
  458.     gWorldBounds.bottom = theGrid->GetGridHeight() * (32 << 16);
  459.     
  460.     
  461. //    Create groups for all of our game's sprites
  462.  
  463.     gFriendlyShipGroup = new TSpriteCollection ();
  464.     FAIL_NIL (gFriendlyShipGroup, "\pcouldn't create friendly ship group")
  465.     gEnemyShipGroup = new TSpriteCollection ();
  466.     FAIL_NIL (gEnemyShipGroup, "\pcouldn't create enemy ship group")
  467.     gFriendlyShotGroup = new TSpriteCollection ();
  468.     FAIL_NIL (gFriendlyShotGroup, "\pcouldn't create friendly shot group")
  469.     gEnemyShotGroup = new TSpriteCollection ();
  470.     FAIL_NIL (gEnemyShotGroup, "\pcouldn't create enemy shot group")    
  471.     
  472.  
  473. // Create the good guy ship
  474.     
  475.     shipData.spriteData.spriteType = TShipSprite::kSpriteType;
  476.     shipData.spriteData.initialX = (100) << 16;
  477.     shipData.spriteData.initialY = (100) << 16;
  478.     shipData.spriteData.initialXVelocity = 0;
  479.     shipData.spriteData.initialYVelocity = 0;
  480.     shipData.spriteData.visibility = kVisible;
  481.     shipData.spriteData.face = 0;
  482.     shipData.spriteData.collectionID = 1000;
  483.     shipData.spriteData.preloadedCollection = gShipGraphics;
  484.     shipData.rotateInterval = 1;
  485.     shipData.shotInterval = 1;
  486.     shipData.shotsGroup = gFriendlyShotGroup;
  487.     
  488.     gFriendlyShip = new TShipSprite(&shipData);
  489.     gFriendlyShip->AddToGroup (gFriendlyShipGroup);
  490.     
  491.     // Create the enemy ship
  492.     // We are using the same graphics here, but this can be changed if you want
  493.     // a separate villain.
  494.     enemyData.spriteData.spriteType = TEnemySprite::kSpriteType;
  495.     enemyData.spriteData.initialX = (1200) << 16;
  496.     enemyData.spriteData.initialY = (1200) << 16;
  497.     enemyData.spriteData.initialXVelocity = 0;
  498.     enemyData.spriteData.initialYVelocity = 0;
  499.     enemyData.spriteData.visibility = kVisible;
  500.     enemyData.spriteData.face = 0;
  501.     enemyData.spriteData.collectionID = 1000;
  502.     enemyData.spriteData.preloadedCollection = gShipGraphics;
  503.     enemyData.rotateInterval = 1;
  504.     enemyData.shotInterval = 1;
  505.     enemyData.shotsGroup = gEnemyShotGroup;
  506.         
  507.     gEnemyShip = new TEnemySprite(&enemyData);
  508.     gEnemyShip->AddToGroup (gEnemyShipGroup);
  509.     
  510.     return noErr;
  511.     
  512.     error:
  513.     if (theErr == noErr)
  514.         theErr = paramErr;
  515.     return theErr;
  516. }
  517.  
  518.  
  519. OSStatus RunGame (void)
  520. {
  521.     // Is game currently running?
  522.     Boolean            running = true;
  523.     
  524.     // We need a separate boolean to determine is the sound key is still being held down -- if it is, we
  525.     // don't toggle.  Otherwise we'd keep turning the sound off and on.
  526.     Boolean         soundKeyDown = false;
  527.     
  528.     OSStatus        theErr = noErr;
  529.  
  530.     //DrawSprocket will return us a CGrafPtr to draw into.
  531.     CGrafPtr        buffer;
  532.     
  533.     //The center of the tile grid is tracked by a camera position.  In this version of the code, this
  534.     //position is identical to the ship, but it could actually be any arbitrary point.  Missile-CAM!
  535.     SInt32            cameraX, cameraY;
  536.     SInt32            tileTop, tileLeft;
  537.     Rect            clipping = {0,0,480,640};
  538.     
  539. #if qShowTiming
  540.     UnsignedWide    startMicroSec, endMicroSec;
  541.     Str255             theString;
  542.     long            frame;
  543. #endif
  544.     
  545.     // We play a beep to signal the start of the game.
  546.     SysBeep(2);
  547.     HideCursor();
  548.     // Basic structure of the game loop is:
  549.     //  * erase all sprites
  550.     //  * draw all sprites in their new locations
  551.     //  * refresh the screen
  552.     //  * move and hit test the sprites based on any queued commands.
  553.     
  554.     // Erasing and drawing is relative to a clipping rectangle and a graphics scale.
  555.     // If at all possible, keep different clipping areas in the game from overlapping.
  556.     // failing that, do all erases in all clip areas before any sprites are drawn.
  557.     
  558.     // For the current version (fixed map), all scaling data is constant, so we can 
  559.     // set it once and ignore it.  If we are changing the scaling data, then we need to
  560.     // move this section into the loop.  Note that scaling data will be different
  561.     // for erases and draws in that case.
  562.  
  563.     // Set the initial camera location.
  564.     gFriendlyShip->GetCurrentWorldLocation(&cameraX, &cameraY);
  565.  
  566. #if __profile__
  567.     ProfilerInit(collectDetailed, PPCTimeBase, 200, 30);
  568. #endif
  569.  
  570. #if qShowTiming
  571.     Microseconds(&startMicroSec);
  572. #endif
  573.  
  574. #if qSyncTiming    
  575.     theTimer.AcceptCommands(true);
  576. #endif
  577.  
  578.     while (running)
  579.     {
  580.         /*************   Point at the Current Buffer    ***************************/
  581.         theErr =  DSpContext_GetBackBuffer( gDrawContext,kDSpBufferKind_Normal, &buffer );
  582.     #if qDebugging
  583.         FAIL_OSERR(theErr, "\p Failed to retrieve a drawing surface.")
  584.     #endif        
  585.  
  586.         SetPort ((GrafPtr) buffer);
  587.         SetDestinationBuffer (buffer->portPixMap, NULL);
  588.         
  589.         /*************   Set the camera location        ***************************/
  590.         
  591.         gFriendlyShip->GetCurrentWorldLocation(&cameraX, &cameraY);
  592.         
  593.         SetBufferClip (&clipping);
  594.         SetWorldOrigin(cameraX, cameraY);
  595.  
  596.  
  597.         /*************   Draw the Tile Grid   ************************/
  598.         tileLeft = (cameraX >> 16) - 320;
  599.         tileTop = (cameraY >> 16) - 240;
  600.         
  601.         theGrid->DrawGrid (&clipping, tileTop,tileLeft);
  602.  
  603.         /**************  Show the time to draw the current frame *******/
  604.     #if qShowTiming
  605.         Microseconds(&endMicroSec);
  606.         WideSubtract((wide *) &endMicroSec, (wide *) &startMicroSec);
  607.         Microseconds(&startMicroSec); // start timing the next frame
  608.         frame = 1000000.0/endMicroSec.lo;
  609.         
  610.         RGBForeColor (&kWhite);
  611.         NumToString (frame, theString);
  612.         MoveTo (10,470);
  613.         DrawString (theString);
  614.         NumToString (gShotsOnBoard, theString);
  615.         MoveTo (10, 450);
  616.         DrawString (theString);
  617.         RGBForeColor (&kBlack);
  618.     #endif    
  619.  
  620.         /*************   Draw all the sprites ***************/
  621.         
  622.         gEnemyShipGroup->DrawSpriteGroup ();
  623.         gFriendlyShipGroup->DrawSpriteGroup ();
  624.         gEnemyShotGroup->DrawSpriteGroup ();
  625.         gFriendlyShotGroup->DrawSpriteGroup ();
  626.  
  627.             
  628.         /*************   Refresh the screen ***************/
  629.             
  630.         theErr =  DSpContext_SwapBuffers( gDrawContext, NULL, NULL);
  631.     #if qDebugging
  632.         FAIL_OSERR (theErr,"\p Failed to swap the buffers.")
  633.     #endif        
  634.  
  635.         
  636.         /*************   Process Commands    *************************************/
  637.  
  638.         // If we are syncing our timing, we'll make sure we have at least one command to process,
  639.         // and then loop until all of the commands are completed.  Otherwise we just grab one key
  640.         // state and call through the loop once.
  641.  
  642.     #if qSyncTiming
  643.         // Wait for a command to actually show up
  644.         while (!theTimer.IsCommandWaiting())
  645.             ;
  646.  
  647.         // Process all waiting commands.
  648.         while (theTimer.RetrieveCommand (&gCommandKeys))
  649.     #else
  650.         theTimer.RetrieveCommand (&gCommandKeys);
  651.     #endif
  652.         {        
  653.             gFriendlyShipGroup->ProcessSpriteGroup();
  654.             gEnemyShipGroup->ProcessSpriteGroup();
  655.             gFriendlyShotGroup->ProcessSpriteGroup();
  656.             gEnemyShotGroup->ProcessSpriteGroup();
  657.             
  658.             gFriendlyShotGroup->HitTest (gEnemyShipGroup);
  659.             gEnemyShotGroup->HitTest (gFriendlyShipGroup);
  660.     
  661.             // Check for exit game
  662.             if (CheckKey ((unsigned char *) &gCommandKeys, kExitGame))
  663.                 running = false;
  664.                 
  665.             // Check for debugger break
  666.             if (CheckKey ((unsigned char *) &gCommandKeys, kDebugger))
  667.             {
  668.                 FlushEvents (everyEvent,0);
  669.                 Debugger();
  670.             }
  671.     
  672.     #if qUsingSound        
  673.             // Give the sound API some time to service tasks.
  674.             HY_ServiceTasks();    
  675.             
  676.             // play some sounds.
  677.             if (CheckKey ((unsigned char *) &gCommandKeys, kOne) && gPlayingSound)
  678.             {
  679.                 HY_PlaySoundHandle (1, gSounds[0], NULL, 0, false);
  680.             }
  681.             if (CheckKey ((unsigned char *) &gCommandKeys, kTwo)  && gPlayingSound)
  682.             {
  683.                 HY_PlaySoundHandle (1, gSounds[1], NULL, 0, false);
  684.             }
  685.             if (CheckKey ((unsigned char *) &gCommandKeys, kThree)  && gPlayingSound)
  686.             {
  687.                 HY_PlaySoundHandle (1, gSounds[2], NULL, 0, false);
  688.             }
  689.             if (CheckKey ((unsigned char *) &gCommandKeys, kFour)  && gPlayingSound)
  690.             {
  691.                 HY_PlaySoundHandle (1, gSounds[3], NULL, 0, false);
  692.             }
  693.                 
  694.             // toggle the sound setting
  695.             if (CheckKey ((unsigned char *) &gCommandKeys,kSoundToggle))
  696.             {
  697.                 if (!soundKeyDown)
  698.                 {
  699.                     soundKeyDown = true;
  700.                     gPlayingSound = !gPlayingSound;
  701.                     if (!gPlayingSound)
  702.                     {
  703.                     HY_StopSample(1);
  704.                     HY_StopSample(2);
  705.                     HY_StopSample(3);
  706.                     HY_StopSample(4);
  707.                     }
  708.                 }
  709.             }
  710.             else
  711.             {
  712.                 soundKeyDown = false;
  713.             }
  714.         #endif        
  715.         }
  716.     }
  717.     
  718.     
  719. #if __profile__
  720.     ProfilerSetStatus(false);
  721.     ProfilerDump("\pMoofWarsDump");
  722.     ProfilerTerm();
  723. #endif
  724.     
  725.     ShowCursor();
  726.     // We want to flush all the keyboard events we haven't been taking.
  727.     theTimer.AcceptCommands (false);
  728.     FlushEvents(everyEvent, 0);
  729.     
  730.     return noErr;
  731. #if qDebugging
  732.     error:
  733.     return theErr;
  734. #endif
  735. }
  736.  
  737. OSStatus CloseGame (void)
  738. {
  739.     // This doesn't clean up everything just yet, but it should!
  740.  
  741.     if (gFriendlyShipGroup != NULL)
  742.     {
  743.         delete gFriendlyShipGroup;
  744.         gFriendlyShipGroup = NULL;    
  745.     }
  746.     
  747.     if (gEnemyShipGroup != NULL)
  748.     {
  749.         delete gEnemyShipGroup;
  750.         gEnemyShipGroup = NULL;
  751.     }
  752.         
  753.     if (gFriendlyShotGroup != NULL)
  754.     {
  755.         delete gFriendlyShotGroup;
  756.         gFriendlyShotGroup = NULL;
  757.     }
  758.         
  759.     if (gEnemyShotGroup != NULL)
  760.     {
  761.         delete gEnemyShotGroup;
  762.         gEnemyShotGroup = NULL;
  763.     }
  764.  
  765.     if (theGrid != NULL)
  766.     {
  767.         delete theGrid;
  768.         theGrid = NULL;
  769.     }
  770.  
  771.     if (gShipGraphics != NULL)
  772.     {
  773.         gShipGraphics->DisposeReference;
  774.         gShipGraphics = NULL;
  775.     }
  776.         
  777.     if (gShotGraphics != NULL)
  778.     {
  779.         gShotGraphics->DisposeReference;
  780.         gShotGraphics = NULL;
  781.     }
  782.     
  783.     if (gDrawContext != NULL)
  784.     {
  785.         DSpContext_SetState (gDrawContext, kDSpContextState_Inactive);
  786.         DSpContext_Release( gDrawContext);
  787.         DSpShutdown();
  788.     }
  789.  
  790.     if (gAppColorTable)
  791.         DisposeCTable (gAppColorTable);
  792.  
  793.     return noErr;
  794. }
  795.  
  796.  
  797. Boolean CheckKey (unsigned char *PtrToKeyMap, short theKey)
  798. {
  799.     unsigned char    theByte, theBit;
  800.     short            byteIndex;
  801.     
  802.     byteIndex = theKey >> 3;
  803.     theByte = *(unsigned char *)(PtrToKeyMap + byteIndex);
  804.     theBit = 1L<<(theKey & 7);
  805.     
  806.     return (theByte & theBit);
  807. }
  808.